#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP

#include "log.hpp"
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <cstdio>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>   // 网络四件套，这四个头文件
#include <unistd.h>

#define SIZE 1024

class Udpserver
{
public:
    Udpserver(uint16_t port, std::string ip = "")
        :_port(port)
        ,_ip(ip)
        ,_sock(-1)
    {}

    bool initServer()
    {
        // 从这里开始，就是新的系统调用，来完成网络的功能
        // 1.创建套接字
        _sock = socket(AF_INET, SOCK_DGRAM, 0);  // 有人会把 AF_INET -> PF_INET 这里是一样的
        if (_sock < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno)); // errno 错误码
            exit(2);   // 退出码应该写清楚，1代表什么，2代表什么？
        }

        // 2.bind绑定:将用户设置的id和port在内核中和我们当前的进程强关联
        // "192.168.1.3" -> 点分十进制字符串风格的IP地址   给人看的
        // 每一个区域的取值范围是[0 - 255] 1字节 -> 4个区域
        // 理论上，表示一个IP地址，四字节就够了!!  -- 网络里是4字节
        // 点分十进制字符串风格的IP地址 <--> 4字节
        struct sockaddr_in local;
        bzero(&local, sizeof local); // 清零
        local.sin_family = AF_INET; // 网络协议
        // 发消息的时候，我需要把我的IP和port给你呢？  需要啊，我要给你回消息
        // 服务器的Ip和端口未来也是要发给对方主机的  ->  先要将数据发送到网络！
        local.sin_port = htons(_port); // 端口号  需要把信息发送给网络，转成大端
        // 1.同上，先要将字符串风格的IP地址转成四字节IP地址
        // 2.4字节主机序列 -> 网络序列
        // 有一套接口直接做完上面的两件事, 让服务器再工作过程中，可以从任意IP中获取数据
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str()); // inet_addr帮我们把点分十进制字符串转成4进制，再转成网络序列
                    // INADDR_ANY 是空，代表可以从任意IP中获取数据. 如果你要bind具体IP使用inet_addr绑定即可

        if (bind(_sock, (struct sockaddr*)&local, sizeof local) < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno)); // errno 错误码
            exit(2); 
        }
        logMessage(NORMAL, "init udp server done ..." "%s", strerror(errno));
        // done
        return true;
    }

    void Start()
    {
        // 作为一款网络服务器，永远不退出的！
        // 服务器启动 -> 进程(不退出)  ->  常驻进程 -> 永远再内存中存在，除非挂了
        // echo server: client给我们发送消息,我们原封不动返回
        char buffer[SIZE];
        for( ; ;)
        {
            // peer,纯输出型参数
            struct sockaddr_in peer;
            bzero(&peer, sizeof(peer));
            // 输入：peer缓冲区大小
            // 输出：实际读到的peer
            socklen_t len = sizeof(peer); // 32为整数
            char result[256];
            std::string cmd_echo;

            // start: 读取数据
            ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) -1, 0, (struct sockaddr*)&peer, &len);

            if (s > 0)
            {
                buffer[s] = 0; // 我们目前数据当作字符串
                // 1. 输出发送的数据信息
                // 2. 是谁发的信息
                // 你发过来的字符串是指令 ls -a -l   rm - rf ~
                if (strcasestr(buffer, "rm") != nullptr || strcasestr(buffer, "rmdir") != nullptr)
                {
                    std::string err_mssage = "坏人....: ";
                    std::cout << err_mssage << buffer << std::endl;
                    sendto(_sock, err_mssage.c_str(), err_mssage.size(), 0, (struct sockaddr*)&peer, len);
                    continue;
                }
                FILE* fp = popen(buffer, "r");
                if (fp == nullptr)
                {
                    logMessage(ERROR, "popen: %d:%s", errno, strerror(errno));
                    continue;
                }

                while (fgets(result, sizeof(result), fp) != nullptr)
                {
                    cmd_echo += result;
                }
                fclose(fp);
            }
            // 分析和处理数据 TODO
            // end: 写回数据 sendto
            //sendto(_sock, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, len);
            sendto(_sock, cmd_echo.c_str(), cmd_echo.size(), 0, (struct sockaddr*)&peer, len);
        }
    }

    ~Udpserver()
    {
        if (_sock >= 0) close(_sock);
    }
private:
    // 一个服务器，一般必须要有ip地址和port(16位整数)
    std::string _ip;
    uint16_t _port;
    int _sock; // socket的返回值，初始给-1，文件描述符
};
#endif